﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Xml;
using System.ComponentModel;

namespace Catsland.Core
{
    public class GameObject : Drawable
    {
        public string _guid = "";
		private string _name = "UntitledGameObject";
        [CategoryAttribute("Basic")]
        public string Name
        {
            set { 
                _name = value;
                if (Mgr<GameEngine>.Singleton._gameEngineMode == GameEngine.GameEngineMode.MapEditor)
                {
                    Mgr<GameEngine>.Singleton.Editor.GameObjectNameChanged();
                }
            }
            get { return _name; }
        }
        private Vector2 _position;  // in-game world space, will be scaled by view point
        [CategoryAttribute("Location")]
        public Vector2 Position
        {
            set { _position = value; }
            get { return _position; }
        }
		private float _height;
        [CategoryAttribute("Location")]
        public float Height
        {
            set { _height = value; }
            get { return _height; }
        }

        private Dictionary<string, CatComponent> _components;
		private VertexPositionColor[] _vertex;
		private VertexBuffer _vertexBuffer;

		public GameObject()
		{
            if (Mgr<GameEngine>.Singleton._gameEngineMode == GameEngine.GameEngineMode.MapEditor)
            {
                _vertex = new VertexPositionColor[4];
                _vertex[0] = new VertexPositionColor(new Vector3(-0.1f, 0.0f, 0.0f), Color.White);
                _vertex[1] = new VertexPositionColor(new Vector3(0.1f, 0.0f, 0.0f), Color.White);
                _vertex[2] = new VertexPositionColor(new Vector3(0.0f, 0.1f, 0.0f), Color.White);
                _vertex[3] = new VertexPositionColor(new Vector3(0.0f, -0.1f, 0.0f), Color.White);

                _vertexBuffer = new VertexBuffer(Mgr<GraphicsDevice>.Singleton, typeof(VertexPositionColor),
                    4, BufferUsage.None);
                _vertexBuffer.SetData<VertexPositionColor>(_vertex);
            }

            // assign a random id
            _guid = Guid.NewGuid().ToString();
		}

        public bool Initialize(Scene scene)
        {
            // initialize all components
            if (_components != null)
            {
                foreach (KeyValuePair<string, CatComponent> key_value in _components)
                {
                    key_value.Value.Initialize(scene);
                }
            }
            return true;
        }

		public void Update(int lastTimeFrame)
		{
            if (_components != null)
            {
                foreach (KeyValuePair<string, CatComponent> key_value in _components)
                {
                    key_value.Value.Update(lastTimeFrame);
                }
            }
		}

        public void EditorUpdate(int lastTimeFrame)
        {
            if (_components != null)
            {
                foreach (KeyValuePair<string, CatComponent> key_value in _components)
                {
                    key_value.Value.EditorUpdate(lastTimeFrame);
                }
            }
        }

        void Drawable.Draw(int timeLastFrame)
        {
            // debug information
            if (Mgr<GameEngine>.Singleton._gameEngineMode == GameEngine.GameEngineMode.MapEditor)
            {
                BasicEffect effect = Mgr<DebugTools>.Singleton.DrawEffect;
                effect.View = Mgr<Camera>.Singleton.m_view;
                effect.Projection = Mgr<Camera>.Singleton.m_projection;
                effect.VertexColorEnabled = false;
                effect.World = Matrix.CreateTranslation(
                    _position.X, _position.Y * Mgr<Scene>.Singleton._yCos, 0.0f);
                effect.DiffuseColor = new Vector3(0.3f, 0.0f, 0.0f);

                Mgr<GraphicsDevice>.Singleton.SetVertexBuffer(_vertexBuffer);
                foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                {
                    pass.Apply();
                    Mgr<GraphicsDevice>.Singleton.DrawUserPrimitives<VertexPositionColor>(
                        PrimitiveType.LineList,
                        _vertex,
                        0,
                        2);
                }
            }
        }

		public override string ToString()
		{
			return Name;
		}

		public GameObject CloneGameObject()
		{
			GameObject gameObject = new GameObject();
			gameObject.Name = new String(_name.ToCharArray());
			gameObject.Position = new Vector2(_position.X, _position.Y);
			gameObject.Height = _height;
            // guid should not be cloned

			// Components
            foreach (KeyValuePair<string, CatComponent> keyValue in _components)
            {
                gameObject.AddComponent(keyValue.Key, keyValue.Value.CloneComponent(gameObject));
            }
			return gameObject;
		}

		public bool SaveToNode(XmlNode node, XmlDocument doc)
		{
			XmlElement gameObject = doc.CreateElement("GameObject");
			node.AppendChild(gameObject);

            // name and guid
			gameObject.SetAttribute("name", _name);
            gameObject.SetAttribute("guid", _guid);

			// position
			XmlElement position = doc.CreateElement("Position");
			gameObject.AppendChild(position);
			position.SetAttribute("X", "" + _position.X);
			position.SetAttribute("Y", "" + _position.Y);
			position.SetAttribute("Z", "" + _height);

            // components
            XmlElement components = doc.CreateElement("Components");
            gameObject.AppendChild(components);

            if(_components != null)
            {
                foreach (KeyValuePair<string, CatComponent> keyValue in _components)
                {
                    keyValue.Value.SaveToNode(components, doc);
                }
            }
            
			return true;
		}

		public static GameObject LoadFromNode(XmlNode node, Scene scene)
		{
			XmlElement gameObject = (XmlElement)node;

            // name and guid
            GameObject newGameObject = new GameObject();
			String name = gameObject.GetAttribute("name");
            String guid = gameObject.GetAttribute("guid");	
			newGameObject._name = name;
            newGameObject._guid = guid;

            // location
			XmlElement position = (XmlElement)gameObject.SelectSingleNode("Position");
			Vector2 postionXY = new Vector2(float.Parse(position.GetAttribute("X")),
										float.Parse(position.GetAttribute("Y")));
			float positionHeight = float.Parse(position.GetAttribute("Z"));
			newGameObject._position = postionXY;
			newGameObject._height = positionHeight;

            // components
            XmlNode components = gameObject.SelectSingleNode("Components");
            foreach (XmlNode component_node in components.ChildNodes)
            {
                string component_name = component_node.Name;
                CatComponent component = CatComponent.LoadFromNode((XmlElement)component_node, scene, newGameObject);
                newGameObject.AddComponent(component.GetType().Name, component);   
            }          
			return newGameObject;
		}

		public void Destroy()
		{
            if (_components != null)
            {
                foreach (KeyValuePair<string, CatComponent> keyValue in _components)
                {
                    keyValue.Value.Destroy();
                }
            }

            if (Mgr<GameEngine>.Singleton._gameEngineMode == GameEngine.GameEngineMode.MapEditor)
            {
                Mgr<Scene>.Singleton._debugDrawableList.RemoveItem(this);
            }
		}

		public float GetDepth()
		{
            // TODO: maybe set Height as a factor is more suitable?
			return _position.Y;
		}

        // this function is used to sort gameObject to perform Painter Algorithm
		public int CompareTo(object obj)
		{
			float otherDepth = ((Drawable)obj).GetDepth();
			float thisDepth = GetDepth();
			if (otherDepth > thisDepth)
			{
				return 1;
			}
			else if (otherDepth < thisDepth)
			{
				return -1;
			}
			return 0;
		}
        
        // catsComponent, add, get, remove, and getList
#region
        // add catsComponent
        public void AddComponent(string component_name, CatComponent component)
        {
            if (_components == null)
            {
                _components = new Dictionary<string, CatComponent>();
            }
            _components.Add(component_name, component);
        }

        public Dictionary<string, CatComponent> GetComponentList()
        {
            return _components;
        }

        public CatComponent GetComponent(string component_name)
        {
            if (_components == null)
            {
                return null;
            }
            else
            {
                if (_components.ContainsKey(component_name))
                {
                    return _components[component_name];
                }
                return null;
            }
        }

        public bool RemoveComponent(string component_name)
        {
            if (_components == null)
            {
                return false;
            }
            if (_components.ContainsKey(component_name))
            {
                CatComponent catComponent = _components[component_name];
                _components.Remove(component_name);
                catComponent.Destroy();
                return true;
            }
            return false;
        }
#endregion
    }
}
